package cn.cloud.all.security.web.savedrequest;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;

public class FastHttpDateFormat {

    protected static final SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);

    protected static final SimpleDateFormat[] formats = {
            new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
    };

    /**
     * GMT time zone - all HTTP dates are on GMT
     */
    protected static final TimeZone gmtZone = TimeZone.getTimeZone("GMT");

    static {
        format.setTimeZone(gmtZone);
        formats[0].setTimeZone(gmtZone);
        formats[1].setTimeZone(gmtZone);
        formats[2].setTimeZone(gmtZone);
    }

    /**
     * Instant on which the currentDate object was generated.
     */
    protected static long currentDateGenerated = 0L;

    /**
     * Current formatted date.
     */
    protected static String currentDate = null;

    /**
     * Formatter cache.
     */
    protected static final HashMap<Long, String> formatCache = new HashMap<>();

    /**
     * Parser cache.
     */
    protected static final HashMap<String, Long> parseCache = new HashMap<>();

    public static String formatDate(long value, DateFormat threadLocalformat) {
        String cachedDate = null;
        Long longValue = value;

        try {
            cachedDate = formatCache.get(longValue);
        } catch (Exception ignored) {
        }

        if (cachedDate != null) {
            return cachedDate;
        }

        String newDate;
        Date dateValue = new Date(value);

        if (threadLocalformat != null) {
            newDate = threadLocalformat.format(dateValue);
            synchronized (formatCache) {
                updateCache(formatCache, longValue, newDate);
            }
        } else {
            synchronized (formatCache) {
                newDate = format.format(dateValue);
                updateCache(formatCache, longValue, newDate);
            }
        }

        return newDate;
    }

    public static String getCurrentDate() {
        long now = System.currentTimeMillis();

        if ((now - currentDateGenerated) > 1000) {
            synchronized (format) {
                if ((now - currentDateGenerated) > 1000) {
                    currentDateGenerated = now;
                    currentDate = format.format(new Date(now));
                }
            }
        }

        return currentDate;
    }

    private static Long internalParseDate(String value, DateFormat[] formats) {
        Date date = null;

        for (int i = 0; (date == null) && (i < formats.length); i++) {
            try {
                date = formats[i].parse(value);
            } catch (ParseException ignored) {
            }
        }

        if (date == null) {
            return null;
        }

        return date.getTime();
    }

    public static long parseDate(String value, DateFormat[] threadLocalformats) {
        Long cachedDate = null;

        try {
            cachedDate = parseCache.get(value);
        } catch (Exception ignored) {
        }

        if (cachedDate != null) {
            return cachedDate;
        }

        Long date;
        if (threadLocalformats != null) {
            date = internalParseDate(value, threadLocalformats);
            synchronized (parseCache) {
                updateCache(parseCache, value, date);
            }
        } else {
            synchronized (parseCache) {
                date = internalParseDate(value, formats);
                updateCache(parseCache, value, date);
            }
        }

        if (date == null) {
            return (-1L);
        } else {
            return date;
        }
    }

    @SuppressWarnings("unchecked")
    private static void updateCache(HashMap cache, Object key, Object value) {
        if (value == null) {
            return;
        }

        if (cache.size() > 1000) {
            cache.clear();
        }

        cache.put(key, value);
    }
}
